本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。
購書連結 https://www.tenlong.com.tw/products/9789864344130
讓我們再次重新認識 JavaScript!
阿基米德:「給我一個支點,我就能移動地球。」(Give me a place to stand on, and I will move the Earth.)
在先前的介紹中,我們已經理解了 DOM Node 的類型、以及節點之間的查找與關係。
那麼在今天的介紹裡我們將繼續來說明,如何透過 DOM API 來建立新的節點、修改以及刪除節點。
document.createElement(tagName)
透過 document.createElement()
可以建立一個新的元素:
var newDiv = document.createElement('div');
在建立新的 div
元素 newDiv
後,這時候我們在瀏覽器上還看不到它,直到透過 appendChild()
、insertBefore()
或 replaceChild()
等方法將新元素加入至指定的位置之後才會顯示。
新建立的 newDiv
我們也可以同時對它指定屬性,如:
newDiv.id = "myNewDiv";
newDiv.className = "box";
document.createTextNode()
之前曾介紹過,除了 HTML 元素節點外,還有「文字節點」,那麼 document.createTextNode()
就是用來建立文字節點的方法。
用法很簡單,直接在 document.createTextNode()
加入字串即可。
跟 createElement
一樣的是,新增的 TextNode 在被加入至某個節點前,我們是看不到它的。
var newDiv = document.createElement('div');
// 建立 textNode 文字節點
var textNode = document.createTextNode("Hello world!");
// 透過 newDiv.appendChild 將 textNode 加入至 newDiv
newDiv.appendChild(textNode);
document.createDocumentFragment()
在 DOM 規範的所有節點之中,DocumentFragment
算是最特殊的一種,它是一種沒有父層節點的「最小化文件物件」。 可以把它看作是一個輕量化的 Document
,用如同標準文件一般的方式來保存「片段的文件結構」。
例如,一開始我們有一個 HTML 的容器元素:
<ul id="myList"></ul>
接著我們透過 document.createDocumentFragment()
來建立 DocumentFragment
:
// 取得外層容器 myList
var ul = document.getElementById("myList");
// 建立一個 DocumentFragment,可以把它看作一個「虛擬的容器」
var fragment = document.createDocumentFragment();
for (var i = 0; i < 3; i++){
// 生成新的 li,加入文字後置入 fragment 中。
let li = document.createElement("li");
li.appendChild(document.createTextNode("Item " + (i+1)));
fragment.appendChild(li);
}
// 最後將組合完成的 fragment 放進 ul 容器
ul.appendChild(fragment);
透過操作 DocumentFragment
與直接操作 DOM 最關鍵的區別在於 DocumentFragment
不是真實的 DOM 結構,所以說 DocumentFragment
的變動並不會影響目前的網頁文件,也不會導致回流(reflow)或引起任何影響效能的情況發生。
換言之,當需要進行大量的 DOM 操作時,用 DocumentFragment
的效能會比直接操作 DOM 好很多。
document.write()
如果你跟我一樣在多年前 誤入歧途 開始寫 JavaScript 的話,那你應該對 document.write()
這個方法不陌生。
document
物件要將某內容寫入網頁可以用 write()
方法,當瀏覽器讀取頁面,解析到 document.write()
的時候就會停下來,並且將內容輸出,且不只是單純的字串,也可以是 HTML 的標籤。
我們只要將對應的 HTML 字串傳入:
document.write("<h1>Hello World!</h1>");
甚至,如果要新增一個 <script>
標籤,並指定外部 js 資源也是可以的,但要注意的是:
// 寫成這樣, </script> 會變成目前 script 區塊的結束,導致錯誤
document.write("<script type=\"text\javascript\" src=\"file.js\">" + "</script>");
// 為了避免這個問題,要將結尾的標籤改個寫法 <\/script> 就 ok 了
document.write("<script type=\"text\javascript\" src=\"file.js\">" + "<\/script>");
另外,需要特別注意的是,當網頁已經讀取完成後才執行 document.write()
,則裡面的內容會完全覆蓋掉目前的網頁:
window.onload = function(){
document.write("Hello world!");
};
上面範例的 window.onload
表示網頁已載入完成,此時無論網頁原本有什麼內容,都會被 "Hello world!"
所覆蓋。
關於 window.onload
我們後續介紹「事件」的時候會再回來說明。
上面介紹了很多建立 DOM 節點的方法,除了最後的 document.write
之外,其他都只是單純建立節點,並未輸出至網頁上。
那麼,接下來要介紹的幾個方法,則說明要如何將剛剛建立好的 DOM 節點,置入到我們所指定的位置。
NODE.appendChild(childNode)
透過 appendChild()
方法,可以將指定的 childNode
節點,加入到 Node
父容器節點的末端:
<ul id="myList">
<li>Item 01</li>
<li>Item 02</li>
<li>Item 03</li>
</ul>
<script>
// 取得容器
var myList = document.getElementById('myList');
// 建立新的 <li> 元素
var newList = document.createElement('li');
// 建立 textNode 文字節點
var textNode = document.createTextNode("Hello world!");
// 透過 appendChild 將 textNode 加入至 newList
newList.appendChild(textNode);
// 透過 appendChild 將 newList 加入至 myList
myList.appendChild(newList);
</script>
NODE.insertBefore(newNode, refNode);
insertBefore()
方法,則是將新節點 newNode
插入至指定的 refNode
節點的前面:
<ul id="myList">
<li>Item 01</li>
<li>Item 02</li>
<li>Item 03</li>
</ul>
<script>
// 取得容器
var myList = document.getElementById('myList');
// 取得 "<li>Item 02</li>" 的元素
var refNode = document.querySelectorAll('li')[1];
// 建立 li 元素節點
var newNode = document.createElement('li');
// 建立 textNode 文字節點
var textNode = document.createTextNode("Hello world!");
newNode.appendChild(textNode);
// 將新節點 newNode 插入 refNode 的前方
myList.insertBefore(newNode, refNode);
<script>
NODE.replaceChild(newChildNode, oldChildNode)
replaceChild()
方法,則是將原本的 oldChildNode
替換成指定的 newChildNode
。
<ul id="myList">
<li>Item 01</li>
<li>Item 02</li>
<li>Item 03</li>
</ul>
<script>
// 取得容器
var myList = document.getElementById('myList');
// 取得 "<li>Item 02</li>" 的元素
var oldNode = document.querySelectorAll('li')[1];
// 建立 li 元素節點
var newNode = document.createElement('li');
// 建立 textNode 文字節點
var textNode = document.createTextNode("Hello world!");
newNode.appendChild(textNode);
// 將原有的 oldNode 替換成新節點 newNode
myList.replaceChild(newNode, oldNode);
<script>
NODE.removeChild(childNode)
removeChild
方法,則是將指定的 childNode
子節點移除。
<ul id="myList">
<li>Item 01</li>
<li>Item 02</li>
<li>Item 03</li>
</ul>
<script>
// 取得容器
var myList = document.getElementById('myList');
// 取得 "<li>Item 02</li>" 的元素
var removeNode = document.querySelectorAll('li')[1];
// 將 myList 下的 removeNode 節點移除
myList.removeChild(removeNode);
<script>
相信看完今天的分享,已經有能力可以透過 DOM 提供的 API 來進行節點的新增、修改以及刪除了。
大家也許會發現,無論要對網頁元素做出什麼樣的操作,我們都會基於某個節點出發對吧?
剛好呼應一開始阿基米德的名言:
「給我一個支點,我就能移動地球。」
那麼有了 DOM API 的支援後, JavaScript 也可以說:
「給我一個節點,我就能建立整個網頁世界。」